
#include "Listener.h"
#include "App.h"
#include "Sprite.h"
#include "Util.h"

#include "GameMessages.h"

#include <stack>
#include <iostream>
#include <algorithm>

int								StateVersion=1;

bool isPathToken(char c){
	return ((c>='0'&&c<='9')||(c>='a'&&c<='z')||(c>='A'&&c<='Z')||(c=='_'||c=='/'))?true:false;
}

bool isCrunchableToken(char c){
	return (isPathToken(c)||c=='#'||c=='.'||c=='"'||c=='\\')?true:false;
}

int notfound=std::string::npos;

bool isNumeric(char c){
	return ((c>='0'&&c<='9')||c=='-'||c=='.')?true:false;
}

const char *opchars="()[]{}+-*/=<^>:;,";
int opcl=strlen(opchars);
bool	isOperatorChar(char c){
	for(int i=0;i<opcl;i++)
		if(c==opchars[i])
			return true;
	return false;
}

static const int tt_string=0;
static const int tt_variable=1;
static const int tt_num=2;
static const int tt_preprocessor=3;
static const int tt_operator=4;
static const int tt_eol=5;
static const int tt_eof=6;

class	SSToken{
public:
	int			type;
	int			depth;
	std::string token;
	SSToken(int tdepth,std::string& tok,int typ){
		token=tok;
		type=typ;
		depth=tdepth;
	}
	bool	equals(std::string val,int typ){
		if(token==val&&type==typ){return true;}
		return false;
	}
	std::string formatted(){
		const char* tnames[]={"string","variable","num","path","operator","eol","eof"};
		return format("[%s:%d]%s",tnames[type],depth,token.c_str());
	}
};

int	inferTokenType(int tdepth,std::string& tok)
{
	int type=0;
	//Perform type inference
	if((tok.size()>1)&&(tok[0]=='"'&&tok[tok.length()-1]=='"')){
		type = tt_string;
		//token=token.substr(1,token.length()-2);
	}else if(isNumeric(tok[0]))
		type=tt_num;
	else if(tok[0]=='#')
		type=tt_preprocessor;
	else if(isOperatorChar(tok[0]))
		type=tt_operator;
	else
		type=tt_variable;
	return type;
}

std::vector<SSToken*>			tokenStack;
std::map<std::string,std::map<std::string,SSToken*>>					gameState;
typedef std::vector<std::vector<std::vector<std::vector<SSToken*>>>>	MapCache;
std::map<int,std::map<int,std::map<int,SSToken*>>>						mapDefs;
MapCache						worldMaps;
std::map<std::string,int>		tileDefMap;
Model*							gMapMesh=null;

class	SSGram
{
public:
	SSGram*							parent;
	std::map<std::string,SSGram*>	contents;
	SSToken*						token;
	SSGram(SSToken* tok)
	{	parent=null;
		token=tok;
	}
};

//SSGram	root;

#define	SSTEX_PATH "..\\tgstation13\\icons\\obj\\"

typedef struct{
	int		bounds[4];
	int		depth;
	char*	pixels;
	int width(){return bounds[2];}
	int height(){return bounds[3];}
}texRgn;


bool recursiveTextureReformat(const char*	path,const char*	ext)
{
	std::string catPath=std::string(path)+"dmi_data.txt";
	remove(catPath.c_str());
	FILE*	tf;
	FILE*	cf;
	std::vector<std::string> files;
	if(fopen_s(&cf,catPath.c_str(),"ab")!=0)return false;
	long totalPixelArea=0;
	std::vector<texRgn> rects4;
	std::vector<texRgn> rects3;

	if (!listFiles(path, "*", files))
		return false;
	for (std::vector<std::string>::iterator iter = files.begin(); iter != files.end(); ++iter)
	{
		int epos=iter->rfind(".");
		if(epos==-1)
			continue;
		std::string pext=iter->substr(epos+1,-1);
		if(strcmp(pext.c_str(),ext)!=0)
			continue;
		
		int moo=0;
		Image img;
		std::string txtChunk="";
		int width,height,depth;
		char*	data;

		if(!loadPNGInfo(iter->c_str(),txtChunk,&width,&height,&depth,&data)){
			logstr(format("PNG load failed:%s!\n",iter->c_str()));
			return false;
		}
		logstr(format("Image %s:%d,%d %d!\n",iter->c_str(),width,height,depth));

		texRgn tr={{0,0,width,height},depth,data};
		if(depth==3){rects3.push_back(tr);}
		else if(depth==4){rects4.push_back(tr);}

		if(depth==4)
			totalPixelArea+=width*height;

		//Write the chunk to a file nearby.
		std::string outPath=*iter;
		outPath+=".txt";
		if(fopen_s(&tf,outPath.c_str(),"wb")!=0)
			return false;
		fwrite(txtChunk.c_str(),txtChunk.size(),1,tf);
		fclose(tf);

		std::string tmp=(*iter)+"\n"+txtChunk;
		fwrite(tmp.c_str(),tmp.size(),1,cf);
						
		outPath=*iter;
		outPath+=".png";
		copyfile(iter->c_str(),outPath.c_str());

		std::cout << iter->c_str() << std::endl;
	}
	fclose(cf);
	logstr(format("Total pixel area:%d -> %f\n",totalPixelArea,sqrtf((float)totalPixelArea)));

	bool didSwap=false;
	int maxx=0;
	int maxy=0;
	do{
		didSwap=false;
		maxx=0;maxy=0;
		//Bubble sort the rects by height....
		for(unsigned int i=0;i<rects4.size()-1;i++){
			if(rects4[i].height()<rects4[i+1].height()){
				texRgn t=rects4[i];
				rects4[i]=rects4[i+1];
				rects4[i+1]=t;
				didSwap=true;
			}
		}
		if(!didSwap){
			for(unsigned int i=0;i<rects4.size()-1;i++){
				texRgn t=rects4[i];
				maxx+=t.width();
				if(maxy<t.height())maxy=t.height();
			}
		}
	}while(didSwap);
	
	int pageDim=1024;

	logstr(format("w:%d,h:%d\n",maxx,maxy));
	
//	for(unsigned int i=0;i<rects4.size()-1;i++){
//		logstr(format("w:%d,h:%d\n",rects4[i].width(),rects4[i].height()));
//	}
	//void glTexSubImage2D( GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, void *data);

	return true;
}


bool	loadFileString(const char* path,std::string&	str,bool stripCRs=false){
	FILE* f;
	if(fopen_s(&f,path,"rb"))return false;
	fseek(f,0,SEEK_END);
	int len=ftell(f);
	fseek(f,0,SEEK_SET);
	char* buf=new char[len];
	fread(buf,len,1,f);
	int write=0;
	if(stripCRs){
		/*strip all carriage returns*/
		for(int i=0;i<len;i++){
			buf[write]=buf[i];
			if(buf[i]!='\r')write++;
		}
	}else write=len;
	str=std::string(&buf[0],write);
	delete[] buf;
	fclose(f);
	return true;
}

void	flushTokens(std::vector<SSToken*>&	stack){
	for(unsigned int i=0;i<stack.size();i++){
		SSToken* t=stack[i];
		delete t;
	}
	stack.clear();
}

bool	saveTokenStack(char*	name, std::vector<SSToken*>&	stack){
	FILE*	f;
	if(fopen_s(&f,name,"wb"))return false;
	unsigned int sv=abs(StateVersion);
	fwrite(&sv,4,1,f);
	unsigned int sz=stack.size();
	fwrite(&sz,4,1,f);
	for(unsigned int i=0;i<sz;i++){
		SSToken* tok=stack[i];
		fwrite(&tok->type,4,1,f);
		fwrite(&tok->depth,4,1,f);
		unsigned int len=tok->token.length();
		fwrite(&len,4,1,f);
		fwrite(tok->token.c_str(),len,1,f);
	}
	fclose(f);
	return true;
}

//#pragma optimize( "", on )

bool	loadTokenStack(char*	name,std::vector<SSToken*>&	stack){
	flushTokens(stack);
	std::string fileStr;
	if(!loadFileString(name,fileStr)){
		return false;
	}
	int sz=fileStr.size();
	if(sz<8)
		return false;
	unsigned int remaining=sz-8;

	char*	ts=(char*)fileStr.c_str();
	unsigned int	tversion=0;
	for(int t=0;t<4;t++)((char*)&tversion)[t]=*ts++;
	if(tversion!=StateVersion){
		logstr(format("State format version mismatch.\n"));
		return false;
	}
	unsigned int	tc=0;
	for(int t=0;t<4;t++)((char*)&tc)[t]=*ts++;
	stack.reserve(tc);
	for(unsigned int i=0;i<tc && remaining>=0;i++){
		int ttype;
		int tdepth;
		unsigned int tlen;
		if(remaining<12)
			break;
		for(int t=0;t<4;t++)((char*)&ttype)[t]=*ts++;
		for(int t=0;t<4;t++)((char*)&tdepth)[t]=*ts++;
		for(int t=0;t<4;t++)((char*)&tlen)[t]=*ts++;
		remaining-=12;
		if(remaining<tlen)
			break;
		stack.push_back(new SSToken(tdepth,std::string(ts,tlen),ttype));
		ts+=tlen;
		remaining-=tlen;
	}
	if(stack.size()!=tc){
		logstr("Corrupt tokens!");
		return false;
	}
	return true;
}

//#pragma optimize( "", off )

/*
expand all files to one giant script...
remove comments
tokenize
*/


bool tokenizeFile(const char* root,const char* path){
	std::string fileStr;
	if(!loadFileString((std::string(root)+path).c_str(),fileStr,true)){	//Load CR Stripped file...
		return false;
	}
	unsigned	int l=fileStr.size();
	const char* base=fileStr.c_str();

	bool	inComment=false;
	bool	inLineComment=false;
	bool	inQuote=false;
	bool	inOperator=false;
	bool	inPath=false;
	bool	inDefine=false;
	bool	inInclude=false;
	bool	expressionStarted=false;
	int 	quotedBracketDepth=0;
	std::stack<unsigned int> expressionStack;
	bool	lineStarted=false;
	unsigned int	lineDepth=0;

	unsigned int	operatorStart=0;
	unsigned int	quoteStart=0;
	unsigned int	pathStart=0;

	SSToken* lastToken=null;

	for(unsigned int i=0;i<l;i++){
		char* b=&fileStr[i];

		if(!lineStarted){
			if(b[0]=='\t'){
				lineDepth++;
				continue;
			}else
				lineStarted=true;
		}
		
		if(!inQuote){
			if(fileStr[i]=='"'){
				inQuote=true;
				quoteStart=i;
			}
		}else{
			if(fileStr[i]=='"'&&fileStr[i-1]!='\\'&&(quotedBracketDepth==0)){
				//end of quote
				inQuote=false;
				tokenStack.push_back(lastToken=new SSToken(lineDepth,fileStr.substr(quoteStart,(i+1)-quoteStart),tt_string));
				
				if(inDefine){
					inDefine=false;
				}
				if(inInclude){
					inInclude=false;
					std::string sb=lastToken->token.substr(1,lastToken->token.length()-2);
					tokenStack.push_back(lastToken=new SSToken(lineDepth,std::string("eol"),tt_eol));
					lineStarted=false;
					lineDepth=0;
					i++;//Skip eol
					tokenizeFile(root,sb.c_str());	//Parse included file...
				}
				continue;//Swallow the closing quote.
			}else{
				if(fileStr[i]=='['){
					quotedBracketDepth++;
				}else if(fileStr[i]==']'){
					quotedBracketDepth--;
				}
			}
		}
		if(inComment==true){	//Consume comments
			if(i<l-1){
				if(b[0]=='*'&&b[1]=='/'){
					inComment = false;
					i++;
					continue;
				}
			}
		}else{
			if(i<l-1){
				if(b[0]=='/'&&b[1]=='*' && (!inLineComment)){
					inComment = true;
					i++;
					continue;
				}
			}
		}

		if(inLineComment==false){ //Consume line comments
			if(inComment==false){
				if(i<l-1 && (b[0]=='/'&&b[1]=='/')){
					inLineComment=true;
					i++;
					continue;
				}
			}
		}
		else{
			if(b[0]!='\n')
				continue;
			else
				inLineComment=false;	//Got to end of comment line.. eat newline and continue
		}

		if(!inPath){
			if(isCrunchableToken(fileStr[i])){
				if(!inQuote){
					if(inOperator){
						inOperator=false;
						std::string op=fileStr.substr(operatorStart,i-operatorStart);
						if(op.size()==1){
							tokenStack.push_back(lastToken=new SSToken(lineDepth,op,tt_operator));
						}else{
							int tstrt=0;
							for(unsigned int mi=2;mi<=op.size();mi+=2){
								std::string top=op.substr(tstrt,mi-tstrt);
								
								if(top=="+="||top=="-="||top=="*="||top=="/="||top=="^="||top=="&="||top=="++"||top=="--"){
									tokenStack.push_back(lastToken=new SSToken(lineDepth,top,tt_operator));
								}else{
									tokenStack.push_back(lastToken=new SSToken(lineDepth,top.substr(0,1),tt_operator));
									tokenStack.push_back(lastToken=new SSToken(lineDepth,top.substr(1,1),tt_operator));
								}
								tstrt+=2;
							}
						}
					}
					inPath=true;
					pathStart=i;
				}
			}
		}else{
			if(!isCrunchableToken(fileStr[i])){
				inPath=false;
				tokenStack.push_back(lastToken=new SSToken(lineDepth,fileStr.substr(pathStart,i-pathStart),tt_variable));
				if(lastToken->token=="#define"){
					inDefine=true;
					continue;
				}else if(lastToken->token=="#include"){
					inInclude=true;
					continue;
				}
			}
		}

		if(!inLineComment){
			if(b[0]=='/'&&b[1]=='/'){
				inLineComment=true;
			}
		}
		
		bool isop=isOperatorChar(fileStr[i]);
		if(!inOperator){
			if(isop && (inPath==false&&inQuote==false&&inLineComment==false&&lineStarted==true) && b[0]!='/'){
				if(i>0&&i<fileStr.length()-1){
					if(fileStr[i]!='/'||(!(fileStr[i+1]=='/'||fileStr[i-1]=='/'))){
						inOperator=true;
						operatorStart=i;
						continue;
					}
				}
			}
		}else{
			if(isop&&!(inQuote||inLineComment||inComment)){
				if((b[0]=='/')&&(operatorStart<i)){
					// "x/" is a bogus operator
					tokenStack.push_back(lastToken=new SSToken(lineDepth,fileStr.substr(operatorStart,i-operatorStart),tt_operator));
					inOperator=false;
				}
			}else{
				inOperator=false;
				tokenStack.push_back(lastToken=new SSToken(lineDepth,fileStr.substr(operatorStart,i-operatorStart),tt_operator));
			}
		}


		if(b[0]=='\n'){
			if((!lastToken)||(lastToken&&lastToken->type!=tt_eol)){
				tokenStack.push_back(lastToken=new SSToken(lineDepth,std::string("eol"),tt_eol));
			}
			lineDepth=0;
			lineStarted=false;
			if((!lastToken)||(lastToken->type!=tt_eol)){
				tokenStack.push_back(lastToken=new SSToken(lineDepth,std::string("eol"),tt_eol));
			}
			lineStarted=false;
			lineDepth=0;
		}
		//if(tokenStack.size()>400)break;
	}
	return true;
}

bool	parseWorld(const char* root,const char* path,SSGram&	gram){
	
	return true;
}

typedef std::map<std::string,int>	TokenMap;
TokenMap							tileStates;

void explodePath(std::string path,std::vector<std::string>& parts){
	int ps=0;
	int psz=path.size();
	for(int i=0;i>=0&&i<(psz-1);){
		i=path.find("/",ps+1);
		parts.push_back(path.substr(ps,i-ps));
		ps=i;
	}
}


int		evalTuple(std::string key,int	ti,TokenMap& context);


int		evalSExpr(std::string key,int	ti,TokenMap& context){
	std::string curvar;
	std::string op;
	bool	gotVar=false;
	bool	gotOp=false;
	bool	gotOpenParen=false;

	while(!(tokenStack[ti]->equals("}",tt_operator)||
			tokenStack[ti]->equals(",",tt_operator)||
			tokenStack[ti]->equals(")",tt_operator)||
			tokenStack[ti]->type==tt_eol)
		){
		if(tokenStack[ti]->type==tt_operator){
			if(tokenStack[ti]->token=="{"){
				if(gotOpenParen){
					ti=evalSExpr(key,ti+1,context);
					gotOpenParen=false;
				}else
				{
					gotOpenParen=true;
				}
			}
			else if(tokenStack[ti]->token=="("){
				ti=evalTuple(key,ti+1,context);
			}
		}else if(tokenStack[ti]->type==tt_variable){
			if(gotVar==false){
				curvar=key+tokenStack[ti]->token;
				gotVar=true;
			}else if(gotOp){
				if(op=="="){
					context[curvar]=ti;
				}else if(op==";"){
					//Expression separator..
				}else{
					logstr(format("Unknown operator:%s skipping\n",op.c_str()));
				}
				gotVar=false;
				gotOp=false;
			}
		}else if(tokenStack[ti]->type==tt_operator)
		{
			if(gotOp==false){
				op=tokenStack[ti]->token;
				gotOp=true;
			}
		}
		ti++;
	}
	return ti;
}

int		evalTuple(std::string key,int	ti,TokenMap& context){
	bool	gotOpenParen=false;
	bool	gotVar=false;
	bool	gotOp=false;
	std::string op;
	int		firstVar=-1;
	int		elemIndex=0;
	while(!(tokenStack[ti]->type==tt_eol)){
		if(tokenStack[ti]->equals("(",tt_operator)){
			if(gotOpenParen){
				ti=evalTuple(key,ti,context);
			}else
				gotOpenParen=true;
		}else if(tokenStack[ti]->type==tt_operator){
			if(tokenStack[ti]->token==","||tokenStack[ti]->token==")"){
				if(gotVar){
					std::string ekey=key+format("/%d",elemIndex);
					ti=evalSExpr(ekey,firstVar,context);
				}
				if(tokenStack[ti]->token==",")
					elemIndex++;
				else{
					ti++;
					break;//Break out..
				}
				gotVar=false;
			}else{
				gotOp=true;
				op=ti;
			}
		}else if(tokenStack[ti]->type==tt_variable){
			if(!gotVar){
				firstVar=ti;
				gotVar=true;
			
			}else if(op=="="){
				//This is the second var in the assignment..
				//	context[firstVar]
				std::string ekey = key+tokenStack[ti]->token;
				ti=evalSExpr(ekey,ti,context);
			}
		}
		ti++;
	}
	return ti;
}

void	setTerrainMesh(std::vector<vec4>&	pvertices,std::vector<int>&	pindices);

Model* buildStationModel(){
	
	bool	lineStart=true;
	logging(false);//<-----------------log

	
	SpriteDef*	defaultSpriteDef=new SpriteDef();
	if(!loadSpriteDef("testTiles",SPRITE_SHADER,defaultSpriteDef))//txonly
		return false;
	for(unsigned int i=0;i<tokenStack.size();i++){
		if(tokenStack[i]->type==tt_eol){
			//logstr("[eol]\n");
			lineStart=true;
		}else{
			std::string str=lineStart?std::string(tokenStack[i]->depth,'\t'):std::string(" ");
			lineStart=false;
			//logstr(str+tokenStack[i]->formatted());
			//logstr(str+tokenStack[i]->token);
		}
	}
	logging(false);
	SSToken**	scn=&tokenStack[0];
	/*
	while((*scn)->type!=tt_eof){
		if((*scn)->type==tt_operator && ((*scn)->token.size()>2)){
			logstr((*scn)->formatted()+"\n");
		}
		scn++;
	}
	*/

	/********* Find all the object variable tokens **********/
	logging(false);//<-----------------log

	unsigned int il=tokenStack.size();
	SSToken*	curObjDef=null;
	SSToken* ptok=null;
	SSToken* pptok=null;

	for(unsigned int t=0;t<il;t++){
		SSToken*	tok=tokenStack[t];
		if(curObjDef==null){
			if(tok->type==tt_variable && (tok->token.find("/")==0) && tok->depth==0){	// /obj/
				curObjDef=tok;
				//logstr(tok->token+"\n");
			}
		}
		else{
			if(tok->type!=tt_eol && tok->depth!=curObjDef->depth+1 && tok->type==tt_variable){
				curObjDef=tok;
			}else if(pptok && ptok && curObjDef && ptok->type==tt_operator &&( tok->type==tt_string || tok->type==tt_variable)){
				if(pptok->token=="icon"||pptok->token=="icon_state"||pptok->token=="direction"){
					//gameState[curObjDef->token][ptok->token]=tok;
					std::string key=curObjDef->token+"/"+pptok->token;//+ptok->token+tok->token;
					tileStates[key]=t;
					//logstr(key+ptok->token+tok->token+"\n");
				}
			}
		}
		pptok=ptok;
		ptok=tok;
	}
	
	logging(false);//<-----------------log
	unsigned int firstMapTileDef=0;
	for(unsigned int t=0;t<il;t++){
		SSToken*	tok=tokenStack[t];
		if(tok->depth==0 && tok->token=="\"aaa\""){firstMapTileDef=t;logstr("Found tile data..\n");break;}
	}
	int	endOfTileDefs=0;
	for(unsigned int t=firstMapTileDef;t<il;t++){
		SSToken*	tok=tokenStack[t];
		if(tok->type==tt_string){
			//logstr(tok->token+"\n");
			tileDefMap[tok->token]=t;
		}else
			{endOfTileDefs=t;break;}
		while(t<(il-1)&&tok->type!=tt_eol)
			{t++;tok=tokenStack[t];}
	}
	logging(false);//<-----------------log
	/* Locate the tilemap definitions */
	SSToken* mapToken=null;
	int	mapcoord[3]={0,0,0};
	for(unsigned int t=endOfTileDefs;t<il;t++){

		//logstr("Processing:"+tokenStack[t]->token+"\n");
		
		SSToken*	tok=tokenStack[t];
		while((t<(il-1))&&!(tok->type==tt_operator&&tok->token=="(")){
			//logstr("Skipping:"+tok->token+"\n");
			t++;
			tok=tokenStack[t];
		}

		if(tok->token=="("&&tok->type==tt_operator&&(t+6<il)){
			mapcoord[0]=atoi(tokenStack[t+1]->token.c_str());
			mapcoord[1]=atoi(tokenStack[t+3]->token.c_str());
			mapcoord[2]=atoi(tokenStack[t+5]->token.c_str());
			//logstr(std::string("Got tilemap:")+tokenStack[t+1]->token+tokenStack[t+3]->token+tokenStack[t+5]->token+"\n");
			t+=6;
			tok=tokenStack[t];
		}//else
		//	break;
		while(t<(il-1)&&tok->type!=tt_string)
			{t++;tok=tokenStack[t];}
		if(tok->type==tt_string){
			mapToken=tok;
			//logstr("Got tilemap data:");
			//logstr(tok->token);

			mapDefs[mapcoord[0]][mapcoord[1]][mapcoord[2]]=tok;
		}
	}
	/* end tilemapdefs */

	Model*			defaultM=defaultSpriteDef->models[0];
	ModelBuilder	gb;

	std::vector<float>	verts;
	std::vector<float>	uvs;
	std::vector<int>	indices;
	
	logging(true);//<-----------------log
	for(int tz=1;tz<2;tz++){
		SSToken*	mtok=mapDefs[1][1][tz];
		std::string& mtext=mtok->token;
		int org=mtext.find("\n");
		int nxtln=mtext.find("\n",org+1);
		int lnlen=(nxtln-org);
		std::vector<char*>	lines;
		for(unsigned int t=org+1;t+lnlen<=mtext.size();t+=lnlen)
			lines.push_back(&mtext[t]);

		int mapWid=(lnlen-1)/3;
		int mapHite=lines.size();
		logstr(format("mapDim:%d,%d\n",mapWid,mapHite));

		const float gdim=1.0f;
		const int patchDim=32;
		vec3 cent=vec3(gdim*mapWid,3*30.0f*gdim,gdim*mapHite);
		int ox=128;
		int oy=128;
		for(int ty=oy-patchDim;ty<oy+patchDim;ty++){
			char*	ln=lines[ty];
			for(int tx=ox-patchDim;tx<ox+patchDim;tx++){
				vec3 torg=vec3(tx*2.0f*gdim,tz*30.0f*gdim,ty*2.0f*gdim)-cent;

				const float worldScale=gdim*2.0f;
				vec3 vcent=vec3(worldScale*ox*2.0f,worldScale*oy*2.0f,0.0f*gdim);
				vec3 to=(vec3(tx*2.0f,ty*2.0f,0.0f)*worldScale)-vcent;//torg;

				char*	cel=ln+(tx*3);
				std::string key("\"");
				key+=cel[0];
				key+=cel[1];
				key+=cel[2];
				key+="\"";
				std::map<std::string,int>::iterator itor=tileDefMap.find(key);

				
				bool	obstructed=false;
				bool	hasfloor=false;
				if(itor!=tileDefMap.end()){
					int start=itor->second;
					int ti=start;
					SSToken*	tok=tokenStack[++ti];
					while(tok->type!=tt_eol){
						while(tok->type!=tt_variable&&tok->type!=tt_eol){
							ti++;
							tok=tokenStack[ti];
						}
						if(tok->type!=tt_variable){
							logstr(format("Couldn't find variable in:%s",tokenStack[start]->token.c_str()));
						}else{
							//logstr(format("looking up map tile:%s\n",tok->token.c_str()));
							std::vector<std::string>	tpath;

							int ttype=0;
							if(tok->token.find("/turf/simulated/floor")==0){
								ttype=1;
								hasfloor=true;
							}else
							if(tok->token.find("/turf/simulated/wall")==0){
								ttype=2;
								obstructed=true;
							}else
							if(tok->token.find("/turf/space")==0){
								ttype=0;
							}

							explodePath(tok->token,tpath);
							std::string pathsrch="";
							int		icon=-1;
							int		icon_state=-1;
							int		dir=-1;
							for(unsigned int i=0;i<tpath.size();i++){
							//	logstr(format("pth:%s\n",tpath[i].c_str()),true);
								pathsrch+=tpath[i];
								TokenMap::iterator it;
								if((it=tileStates.find(pathsrch+"/icon"))!=tileStates.end()){
									icon=it->second;
								}else{
									//logstr(format("no icon: %s/icon\n",pathsrch.c_str()));
								}
								if((it=tileStates.find(pathsrch+"/icon_state"))!=tileStates.end()){
									icon_state=it->second;
								}else{
									//logstr(format("no icon state: %s/icon_state\n",pathsrch.c_str()));
								}
								if((it=tileStates.find(pathsrch+"/dir"))!=tileStates.end()){
									dir=it->second;
								}else{
									//logstr(format("no icon state: %s/icon_state\n",pathsrch.c_str()));
								}
							}
							SSToken *scn=tokenStack[++ti];
							if(icon<0){
								logstr(format("icon not found:%s\n",tok->token.c_str()));
								TokenMap	context;
								ti=evalSExpr(pathsrch,ti,context);
								scn=tokenStack[ti];
								for(TokenMap::iterator it=context.begin();it!=context.end();it++){
									logstr(format("Building context:%s\n",it->first.c_str(),tokenStack[it->second]->token));
								}
								while(scn->type!=tt_eol){
									logstr(format("remaining tokens:%s\n",scn->token.c_str()));
									scn=tokenStack[++ti];
								}
							}
							else if(icon_state<0){
								logstr(format("icon_state not found:%s\n",tok->token.c_str()));
								TokenMap	context;
								ti=evalSExpr(pathsrch,ti,context);
								scn=tokenStack[ti];
								for(TokenMap::iterator it=context.begin();it!=context.end();it++){
									logstr(format("Building context:%s\n",it->first.c_str(),tokenStack[it->second]->token));
								}

								//while(scn->type!=tt_eol){
								//	logstr(format("remaining tokens:%s\n",scn->token.c_str()));
								//	scn=tokenStack[++ti];
								//}
							}
							//if(tileStates.find(tok->token+"/icon")==tileStates.end()&&
							//	tileStates.find(tok->token+"/icon_state")==tileStates.end()){
							//		logstr(format("Missing icon:%s\n",tok->token.c_str()));
							//}
						
							if(cel[0]!='a'||cel[1]!='a'||cel[2]!='a'){
								if((icon>=0)&&(icon_state>=0)){
									int midx=0;
									logstr(format("%s:%s\n",tokenStack[icon]->token.c_str(),tokenStack[icon_state]->token.c_str()));
									//vec3 torg=vec3(tx*2.0f*gdim,tz*30.0f*gdim,ty*2.0f*gdim)-cent;
									/*
									if(tokenStack[icon]->token=="power_cond_red.dmi"){
									//	torg.y+=gdim*2.0f;
										midx=3;
									}else
									if(tokenStack[icon]->token=="structures.dmi"){
									//	torg.y-=gdim*2.0f;
										midx=4;
									}else{
										midx=0;
									}*/
									midx=ttype;
									if(midx)
									{

										const char*	geomNames[]={"tile_flat","tile_wall"};//"tile_flat","tile_flat2","tile_floor","tile_leftWall","tile_leftWall2","tile_leftDoor","tile_machine","tile_machine2"};
										int	remap[4][6]={{0,1,2,1,1,1},{1,0,2,1,-1,1},{0,1,2,-1,-1,1},{1,0,2,-1, 1,1}};
										int rdir=dir>=0?dir&3:rand()%4;//rand()%3
										//gb.addModel(SpriteDef::getModel(geomNames[0]),to,worldScale,&remap[rdir][0],false);//(rand()&255)%4
										rdir=0;
										gb.addModel(SpriteDef::getModel(geomNames[midx-1]),to,worldScale,&remap[rdir][0],false);//(rand()&255)%4
										
										float bxdim=worldScale*2.0f;
										physicsAddBoxParams	boxdef={to+vec3(0,0,(midx==1)?-worldScale:worldScale),vec3(bxdim,bxdim,bxdim),null};
										Listener::sendMessage("physicsAddBox",&boxdef);

									}
									//gb.addModel(defaultM,torg,gdim);
									//logstr(format("%s:%s\n",tokenStack[icon]->token.c_str(),tokenStack[icon_state]->token.c_str()));
								
									torg.y+=gdim;
								}
							}	
						}						
						ti++;
						tok=tokenStack[ti];
					}
					if(!obstructed){
					}
				}else{
					logstr("Missing def for:"+key);
				}
				if(cel[0]!='a'||cel[1]!='a'||cel[2]!='a')
				{
					if((!obstructed)&&hasfloor){
						if(!(rand()%32)){
							physicsAddRagdollParams	ragdolldef={to+vec3(0,0,worldScale*0.3f),null};
							Listener::sendMessage("physicsAddRagdoll",&ragdolldef);
						}
					}
					//vec3 torg=vec3(tx*2.0f*gdim,tz*30.0f*gdim,ty*2.0f*gdim)-cent;
					//if(cel[0]!='a'||cel[1]!='a'||cel[2]!='a')
					//	torg.y+=5.0f*gdim;
					//torg.y+=((cel[0]&7)+(cel[1]&7)+(cel[2]&7))*gdim;

	//				Sprite* planet=new Sprite();
	//				planet->definition=defaultSpriteDef;
	//				planet->position=torg;//randomSphericalPosition(0.0f,80.0f);
	//				planet->transform = identity4();
					//logstr(format("%s:%s\n",tokenStack[icon]->token.c_str(),tokenStack[icon_state]->token.c_str()));
					//gb.addModel(defaultM,torg,gdim);
					//genMdl->catenateModelAt(defaultM,vec3(tx*2.0f,0.0f,ty*2.0f));
				}
			}
		}
	}
	logging(true);//<-----------------log
	if(gb.hasGeometry()){
		Model*	m=gb.getModel("station13",gApp->getRenderer());
		if(gb.physVertices.size()&&gb.physIndices.size()){
			setTerrainMesh(gb.physVertices,gb.physIndices);
		}

		return m;
	}
	return null;
}

std::vector<SSToken*>	testStack;

bool	SSLoad(const char*	root,const char*	path){
	
/*	if(!recursiveTextureReformat("..\\tgstation13","dmi")){
		flushLog();
		return false;
	}
	flushLog();
	exit(0);
	//TextureID	test;//SSTEX_PATH "aibots.dmi"
	//SSTEX_PATH "wt2f.dmi.png"       "..\\tgstation13\\icons\\88x31.png"
	//		if ((test = gApp->getRenderer()->addTexture(SSTEX_PATH "wt2f.dmi.png", FILTER_POINT)) == TEXTURE_NONE) {
	//			return false;
	//		}


	SSGram	world;
	machineScopes.push_back(std::map<std::string,SSGram*>());
	currentScope=&machineScopes[0];
*/

	if(!loadTokenStack("sstation13.state",tokenStack))
	{
		FILE* f;
		if(fopen_s(&f,(std::string(root)+path).c_str(),"rb"))return false;
		fclose(f);
		bool reslt=tokenizeFile(root,path);
		tokenStack.push_back(new SSToken(0,std::string("eof"),tt_eof));
		if(reslt){
			saveTokenStack("sstation13.state",tokenStack);
		}else
			return false;
	}
	
	Model* m=new Model();
	SpriteDef*	worldSpriteDef=new SpriteDef();
	if(!loadSpriteDef("testTiles","lighting.shd",worldSpriteDef))
		return false;

	Model*	stationModel=new Model();
	Renderer* renderer=gApp->getRenderer();
	if(!loadModel("station13",stationModel) || true){
		delete stationModel;
		stationModel=buildStationModel();
		//stationModel->saveOBJ("station13.obj");
	}else{
		if (!stationModel->makeDrawable(renderer)){
			return false;
		}
	}
	worldSpriteDef->models[0]=stationModel;
	Sprite* wld=new Sprite();
	wld->definition=worldSpriteDef;
	wld->position=vec3(0,0,0);
	wld->transform = identity4();
	flushLog();

 	return true;
}



class	SS13ClassListener : public Listener
{
public:
	virtual bool	message(const char *msg=null,void*	params=null)
	{
		if(msg=="engineStep"){
			//gEngine->step();
		}else if(msg=="engineRenderSolid"){

			//gEngine->render();
		}else if(msg=="engineRender"){

			//gEngine->render();
		}else if(msg=="engineDrawSelected"){
			//engineDrawSelected((Renderer*)params);
		}else if(msg=="engineDoPicking"){
			/*(static bool lWasDown=false;
			static bool rWasDown=false;
			gEngine->leftClicked=gEngine->rightClicked=false;
			if(!gApp->lMouseButton && !gApp->rMouseButton){lWasDown=false;rWasDown=false;return true;}
			if(gApp->lMouseButton && !lWasDown){lWasDown=true; gEngine->leftClicked=true;}
			if(gApp->rMouseButton && !rWasDown){rWasDown=true; gEngine->rightClicked=true;}

				//for(float t=1.0f;t<5.0f;t+=1.0f)gApp->drawLight((camRay*t/2.0f)+cp,dx*1.1f,dy*1.1f,0.1f);//(-camPos)+
			Listener::sendMessage("doSpritePicking");*/
		}else if(msg=="engineBoot"){
			//engineMain((App*)params);
			//gEngine->boot((App*)params);	
			//if(!SSLoad("..\\test\\","test.dme"))
			if(!SSLoad("..\\tgstation13\\","tgstation.dme"))
				return false;
		//if(!SSLoad("..\\tgstation13\\","maps\\tgstation.2.0.8.dmm"))
		//	return false;
			return true;
		}else if(msg=="engineShutdown"){
			//gEngine->shutdown();
		}
		return true;
	}
	SS13ClassListener(){
		Listener::listen(this,"engineStep");
		Listener::listen(this,"engineBoot");
		Listener::listen(this,"engineRender");
		Listener::listen(this,"engineShutdown");
		Listener::listen(this,"engineDoPicking");
		Listener::listen(this,"engineDrawSelected");
	}	
	virtual ~SS13ClassListener(){}
};

SS13ClassListener gSS13ClassListener;
